package com.novoda.runbuddy.provider;

import java.util.HashMap;
import java.util.Map;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;

public class RunsProvider extends IRBContentProvider {

	@SuppressWarnings("unused")
	private static final String			TAG								= RunsProvider.class.getName();
	private static final String			RUN_ROUTE_JOIN					= "run "
																				+ "LEFT OUTER JOIN route ON (run._idRoute = route._id)";

	// A join against runSegment, checkpoint, run
	private static final String			RUN_SEGMENT_JOIN				= "runSegment "
																				+ "LEFT OUTER JOIN run ON (run._id = runSegment._idRun) "
																				+ "LEFT OUTER JOIN checkPoint ON (runSegment._idCheckPoint = checkPoint._id)";

	private static Map<String, String>	RUN_LIST_PROJECTION_MAP			= null;
	private static Map<String, String>	RUN_SEGMENT_LIST_PROJECTION_MAP	= null;

	public RunsProvider(ContentProvider cp, SQLiteOpenHelper mOpenHelper) {
		super(cp, mOpenHelper);
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		int match = URI_MATCHER.match(uri);
		int routeId;
		int runId;
		switch (match) {
			case RUNS:
				routeId = Integer.parseInt(uri.getPathSegments().get(1));
				return insertRun(routeId, values);
			case RUN_SEGMENTS:
				routeId = Integer.parseInt(uri.getPathSegments().get(1));
				runId = Integer.parseInt(uri.getPathSegments().get(3));
				return insertSegmentForRun(runId, routeId, values);
			default:
				throw new IllegalArgumentException("Unknown URL " + uri);
		}
	}

	private Uri insertRun(int routeId, ContentValues initialValues) {
		long rowID;
		ContentValues values;
		if (initialValues != null) {
			values = new ContentValues(initialValues);
		} else {
			values = new ContentValues();
		}

		values.put("_idRoute", routeId);
		Long now = Long.valueOf(System.currentTimeMillis());
		Resources r = Resources.getSystem();

		if (values.containsKey(RunBuddyCP.Runs.NAME) == false) {
			values.put(RunBuddyCP.Runs.NAME, r.getString(android.R.string.untitled));
		}

		if (values.containsKey(RunBuddyCP.Runs.TOTAL_TIME) == false) {
			values.put(RunBuddyCP.Runs.TOTAL_TIME, 0);
		}

		if (values.containsKey(RunBuddyCP.Runs.CREATED_DATE) == false) {
			values.put(RunBuddyCP.Runs.CREATED_DATE, now);
		}

		if (values.containsKey(RunBuddyCP.Runs.MODIFIED_DATE) == false) {
			values.put(RunBuddyCP.Runs.MODIFIED_DATE, now);
		}

		rowID = getWritableDatabase().insert("run", "name", values);
		if (rowID > 0) {
			getWritableDatabase().update("run", values, "_id=" + rowID, null);

			// #Is there a way to do this with UriBuilder?
			String sUri = RunBuddyCP.Runs.CONTENT_URI.toString();
			sUri = sUri.replace("#", "" + routeId);

			Uri uri = ContentUris.withAppendedId(Uri.parse(sUri), rowID);
			getContext().getContentResolver().notifyChange(uri, null);
			return uri;
		}
		throw new SQLException("Failed to insert row into runs");
	}

	private Uri insertSegmentForRun(int runId, int routeId, ContentValues initialValues) {
		long rowID;
		ContentValues values;
		if (initialValues != null) {
			values = new ContentValues(initialValues);
		} else {
			values = new ContentValues();
		}

		values.put("_idRun", runId);
		Long now = Long.valueOf(System.currentTimeMillis());

		if (values.containsKey(RunBuddyCP.RunSegments.CHECKPOINT) == false) {
			throw new SQLException("Checkpoint need to be provided in order to insert segment");
		} else {
			int checkpoint = Integer
					.parseInt(Uri.parse(values.getAsString(RunBuddyCP.RunSegments.CHECKPOINT)).getLastPathSegment());
			values.remove(RunBuddyCP.RunSegments.CHECKPOINT);
			values.put("_idCheckPoint", checkpoint);
		}

		// #auto fill?
		if (values.containsKey(RunBuddyCP.RunSegments.CHECKPOINT_NO) == false) {
			values.put(RunBuddyCP.RunSegments.CHECKPOINT_NO, -1);
		}
		
		// #auto fill?
		if (values.containsKey(RunBuddyCP.RunSegments.TIME) == false) {
			values.put(RunBuddyCP.RunSegments.TIME, -1);
		}
		
		if (values.containsKey(RunBuddyCP.RunSegments.LAP) == false) {
			values.put(RunBuddyCP.RunSegments.LAP, 1);
		}

		if (values.containsKey(RunBuddyCP.RunSegments.CREATED_DATE) == false) {
			values.put(RunBuddyCP.RunSegments.CREATED_DATE, now);
		}

		if (values.containsKey(RunBuddyCP.RunSegments.MODIFIED_DATE) == false) {
			values.put(RunBuddyCP.RunSegments.MODIFIED_DATE, now);
		}

		rowID = getWritableDatabase().insert("runSegment", "name", values);
		if (rowID > 0) {
			getWritableDatabase().update("runSegment", values, "_id=" + rowID, null);

			// #Is there a way to do this with UriBuilder?
			String sUri = RunBuddyCP.RunSegments.CONTENT_URI.toString();
			sUri = sUri.replace("#", "" + routeId);
			sUri = sUri.replace("#", "" + runId);
			
			Uri uri = ContentUris.withAppendedId(Uri.parse(sUri), rowID);
			getContext().getContentResolver().notifyChange(uri, null);
			return uri;
		}
		throw new SQLException("Failed to insert row into runSegment");
	}

	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

		Cursor c = null;

		int match = URI_MATCHER.match(uri);
		switch (match) {
			case RUNS:
				qb.setTables(RUN_ROUTE_JOIN);
				qb.setProjectionMap(RUN_LIST_PROJECTION_MAP);
				break;
			case RUN_ID:
				qb.setTables(RUN_ROUTE_JOIN);
				qb.setProjectionMap(RUN_LIST_PROJECTION_MAP);
				qb.appendWhere("run._id=" + uri.getPathSegments().get(1));
				break;
			case RUN_SEGMENTS:
				qb.setTables(RUN_SEGMENT_JOIN);
				qb.setProjectionMap(RUN_SEGMENT_LIST_PROJECTION_MAP);
				qb.appendWhere("_idRun=" + uri.getPathSegments().get(3));
				qb.setCursorFactory(new CheckPointCursorFactory());
				break;
			default:
				throw new IllegalArgumentException("Unknown URL " + uri);
		}
		c = qb.query(getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder);
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

	@Override
	public int delete(Uri arg0, String arg1, String[] arg2) {
		return 0;
	}

	@Override
	public String getType(Uri arg0) {
		return null;
	}

	@Override
	public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
		return 0;
	}

	static {
		RUN_LIST_PROJECTION_MAP = new HashMap<String, String>();
		RUN_LIST_PROJECTION_MAP.put(RunBuddyCP.Runs.NAME, "run.name");
		RUN_LIST_PROJECTION_MAP.put(RunBuddyCP.Runs.TOTAL_TIME, "totalTime");
		RUN_LIST_PROJECTION_MAP.put(RunBuddyCP.Runs.CREATED_DATE, "createdAt");
		RUN_LIST_PROJECTION_MAP.put(RunBuddyCP.Runs.MODIFIED_DATE, "modifiedAt");
		RUN_LIST_PROJECTION_MAP.put(RunBuddyCP.Runs.DEFAULT_SORT_ORDER, "modifiedAt DESC");

		// RunSegment Projection Map definition
		RUN_SEGMENT_LIST_PROJECTION_MAP = new HashMap<String, String>();
		RUN_SEGMENT_LIST_PROJECTION_MAP.put(RunBuddyCP.RunSegments.CHECKPOINT, "_idCheckPoint");
		RUN_SEGMENT_LIST_PROJECTION_MAP.put(RunBuddyCP.RunSegments.CHECKPOINT_NO, "checkpointNo");
		RUN_SEGMENT_LIST_PROJECTION_MAP.put(RunBuddyCP.RunSegments.TIME, "segmentTime");
		RUN_SEGMENT_LIST_PROJECTION_MAP.put(RunBuddyCP.RunSegments.LAP, "lap");
		RUN_SEGMENT_LIST_PROJECTION_MAP.put(RunBuddyCP.RunSegments.CREATED_DATE, "createdAt");
		RUN_SEGMENT_LIST_PROJECTION_MAP.put(RunBuddyCP.RunSegments.MODIFIED_DATE, "modifiedAt");
		RUN_SEGMENT_LIST_PROJECTION_MAP.put(RunBuddyCP.RunSegments.DEFAULT_SORT_ORDER, "checkPointNo DESC");
	}


}
